package com.dt.app.security.rsa;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.dt.app.exception.CmsTokenException;
import com.dt.app.modules.base.entites.PayloadEntity;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
import org.springframework.stereotype.Component;

import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.Date;

/**
 * 使用非对称秘钥RSA生成Jwt令牌工具类
 * @author DT
 * @date 2021/6/27 17:30
 */
@Component
public class RsaJwtUtils {

    @Value("${jwt.expTime}")
    private int expTime;

    @Value("${jwt.rsaPath}")
    private String rsaPath;

    @Value("${jwt.rsaAlias}")
    private String rsaAlias;

    @Value("${jwt.rsaPassword}")
    private String rsaPassword;

    /**
     * 生成Token令牌
     * @param payloadStr 负载信息（用户信息）
     * @param rsaKey RSA秘钥
     * @return 返回Token令牌
     */
    public String generateTokenByRsa(String payloadStr, RSAKey rsaKey) throws JOSEException {
        // 创建JWS头，设置签名算法和类型
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
                .type(JOSEObjectType.JWT)
                .build();
        // 将负载信息封装到Payload中
        Payload payload = new Payload(payloadStr);
        // 创建JWS对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        // 创建RSA签名器
        JWSSigner jwsSigner = new RSASSASigner(rsaKey, true);
        // 签名
        jwsObject.sign(jwsSigner);
        return jwsObject.serialize();
    }

    /**
     * 校验令牌信息
     * @param token 令牌
     * @param rsaKey RSA秘钥
     * @return 返回负载信息
     */
    public PayloadEntity verifyTokenByRsa(String token, RSAKey rsaKey) throws ParseException, JOSEException {
        // 从token中解析JWS对象
        JWSObject jwsObject = JWSObject.parse(token);
        RSAKey publicRsaKey = rsaKey.toPublicJWK();
        // 使用RSA公钥创建RSA验证器
        JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
        if (!jwsObject.verify(jwsVerifier)) {
            throw new CmsTokenException("Token令牌签名不合法！");
        }
        String payload = jwsObject.getPayload().toString();
        PayloadEntity payloadEntity = JSONUtil.toBean(payload, PayloadEntity.class);
        if (payloadEntity.getExpTime() < System.currentTimeMillis()) {
            throw new CmsTokenException("Token令牌已过期！");
        }
        return payloadEntity;
    }

    /**
     * 生成负载信息
     * @param username 登录账号
     * @return 返回负载对象
     */
    public PayloadEntity getDefaultPayload(String username) {
        Date now = new Date();
        // 设置过期时间5分钟后
        Date exp = DateUtil.offsetSecond(now, expTime);
        return PayloadEntity.builder()
                .subject(username)
                .issueTime(now.getTime())
                .expTime(exp.getTime())
                .jwtId(UUID.randomUUID().toString())
                .username(username)
                .build();
    }

    /**
     * 获取RSA秘钥
     * @return 返回RSA秘钥对象
     */
    public RSAKey getDefaultRsaKey() {
        // 从classpath下获取RSA秘钥对
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource(rsaPath), rsaPassword.toCharArray());
        KeyPair keyPair = keyStoreKeyFactory.getKeyPair(rsaAlias, rsaPassword.toCharArray());
        // 获取RSA公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取RSA私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey).privateKey(privateKey).build();
    }

}
